//Author Domenico Diacono
//12 Luglio 2012

#include <string>
#include <stdlib.h>
#include <sys/time.h>
#include <iostream>
#include <map>


extern "C" { 
#include "lcg_util.h" 
}

#include "libSbNet.h"

using namespace std;
using namespace libconfig;

map<string,int> libSbNet::mapHost;
bool libSbNet::debugStatus = true;
int libSbNet::maxPing = 2;
int libSbNet::pingTimeout = 1;
string libSbNet::supProt;

int libSbNet::loadCfg()
{
        Config cfg;

        //parsing file
        try
        {
                cfg.readFile("libSbNet.cfg");
        }
        catch(const FileIOException &fioex)
        {
                std::cerr << "I/O error while reading file." << std::endl;
                return(EXIT_FAILURE);
        }
        catch(const ParseException &pex)
        {
                std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() << " - " << pex.getError() << std::endl;
                return(EXIT_FAILURE);
        }

  	const Setting& root = cfg.getRoot();

	root.lookupValue("debug",this->debugStatus);
	root.lookupValue("maxping",this->maxPing);
	root.lookupValue("pingtimeout",this->pingTimeout);
	root.lookupValue("supProt",this->supProt);	


        return(EXIT_SUCCESS);
}

string libSbNet::convertInt(int number)
{
   stringstream ss;//create a stringstream
   ss << number;//add number to the stream
   return ss.str();//return a string with the contents of the stream
}

string libSbNet::findStoragePing()
{
	//finds the lowest pingable srm host on catalog, and fills a static map "host:ping value" 
	//returns fastest srm file name

	map<string,int>::iterator it;

	string strHost;	//host name
	string strName; //srm name with path
	string strPreferredSrm;
	string strPing;
	int pingTime = 10000;

	map<string,string>::iterator itPath;
        for ( itPath=mapSrmHost.begin() ; itPath != mapSrmHost.end(); itPath++ )
        {
        	strHost = itPath->second;
		strName = itPath->first;
		int minTime;

		it=mapHost.find(strHost);
		if (it != mapHost.end())
		{
			minTime = it->second;
			if (minTime < pingTime)
			{
				pingTime = minTime;
        	                strPing = strHost;
				strPreferredSrm=strName;
			}	
		} else {
			//inizialize min ping
			mapHost[strHost]=10000;
				
			//ping*********
			FILE *fp;
			char buf[1024];
			int status = 1;
			int ping_rtn = 0;
				
			string strCommand = "ping -c "+convertInt(this->maxPing)+" -w "+convertInt(this->pingTimeout)+" "+strHost; 
			if (debugStatus)
			{
				cout << "pingCommand:" << strCommand << endl;
			}
	
			if (NULL == (fp = popen((strCommand.c_str()), "r"))) {
				fprintf(stderr, "The popen(3) function returned NULL\n");
			} else {
				while (NULL != fgets(buf, sizeof(buf), fp)) {
					char *s;
					if (NULL != (s = strstr(buf, " packet loss"))) {
						status = 0;
						if (NULL != (s = strstr(buf, " 100% packet loss"))) {
							ping_rtn = 1;
							break;
						}
					}
        	                        if (NULL != (s = strstr(buf, " min/avg/max/mdev"))) {
						int counter=0;
						char *p = strtok(s, "/");
						while (p) {
							counter += 1;
							if (counter == 4) {
								string strTimes(p);
								size_t slashPos = strTimes.find("/");
						                string strMin= strTimes.substr(0, slashPos);		
    								minTime = atoi(strMin.c_str());
					
								//map host building...	
							        mapHost[strHost]=minTime;
						
								if (minTime < pingTime) {
									pingTime = minTime;
									strPing = strHost;
									strPreferredSrm=strName;
								} 	
							}
    							p = strtok(NULL, " ");
						}
					 	if (debugStatus){
                					cout << endl << "FSP pingStrHost: " << strHost << endl;
                					cout << endl << "FSP pingMinTime: " << minTime << endl;
        					}
					}
				}
			}
			pclose(fp);
		}
	}

        if (debugStatus){
		cout << endl << "FSP Srm: " << strPreferredSrm << endl;
		cout << endl << "FSP pingTime: " << pingTime << endl;
	}
	return strPreferredSrm;
}

int libSbNet::localCopy(string strSrm, string strDestFile)
{
	//recursive function: copy the file locally and deals with srm errors
	char * vo=getenv("LCG_GFAL_VO");
	int nbstream = 10;
	char *conf_file = NULL;
        int insecure = 0;

        int verbose = 0;
        if (this->debugStatus){
		verbose = 2;
	}	
        char * destFile = const_cast<char*> ( strDestFile.c_str() );
	char * sourceFile = const_cast<char*> ( strSrm.c_str() );
	
	string strOut;	

	enum se_type type = TYPE_SRMv2;
	int nobdii = 0;
	int timeout = 0;
	char *src_spacetokendesc = NULL;
	char *dest_spacetokendesc = NULL;
	char *errbuf = NULL;
	int errbufsz=0;

        if (this->debugStatus){
		cout << "FWS input sourcefile: " << sourceFile << endl;
		cout << "FWS input destfile: " << destFile << endl;
	}

	
	//LOCAL COPY
	int ret = lcg_cp3(sourceFile,destFile,type,type,type,nobdii,vo,nbstream,conf_file,insecure,verbose,timeout,src_spacetokendesc, dest_spacetokendesc, errbuf, errbufsz);

	//temporary erase from mapSrmHost	
        map<string,string>::iterator itPath;
	itPath=mapSrmHost.find(strSrm);
	
	string strTempSrm=itPath->first;
	string strTempHost=itPath->second;
		
	mapSrmHost.erase(itPath);


        if(ret==0) {
	        if (this->debugStatus){
                	cout << "FWS has found a working storage" << endl;
			cout << endl;
			cout << "MAPHOST: " << endl;
                        map<string,int>::iterator itDBHost;
			for ( itDBHost=mapHost.begin() ; itDBHost != mapHost.end(); itDBHost++ )
                	{
				cout << "host :" << itDBHost->first << " pingTime: " << itDBHost->second << endl;
			}	

			cout << endl;
			cout << "MAPPATH: " << endl;
                        map<string,string>::iterator itDBPath;
			for ( itDBPath=mapSrmHost.begin() ; itDBPath != mapSrmHost.end(); itDBPath++ )
                	{
				cout << "Srm :" << itDBPath->first << " host: " << itDBPath->second << endl;
			}	
			cout << endl;
		}
                strOut = strDestFile;
        } else {
                //we have an error on storage for this file: it cannot be downloaded
		//searches the fastest entry in mapHost, still present in mapSrmHost
		string strPreferredSrm = searchFastStorage();

		if (strPreferredSrm.empty())
		{
			ret=1;
		} else {
			//recursive
			ret = localCopy(strPreferredSrm, strDestFile);	
        	}

	        
	}
	
	//re-add the failed srm to the map
	mapSrmHost[strTempSrm]=strTempHost;
	
        
	return ret;
}

string libSbNet::searchFastStorage()
{
	string strPreferredSrm;
	map<string,string>::iterator itPath;
	int pingTime = 10000;
        
	//if mapHost is empty...builds it
        if (mapHost.empty())
        {       
        	findStoragePing();
        }       

	for ( itPath=mapSrmHost.begin() ; itPath != mapSrmHost.end(); itPath++ )
        {
        	map<string,int>::iterator itHost;
                        
                //searches the host  
                itHost = mapHost.find(itPath->second);
                        
                if(itHost != mapHost.end())
                {       
                	//finds the min ping time
                	int intTime = itHost->second;
                	if (intTime < pingTime)
                	{
                		pingTime = intTime;
                                strPreferredSrm=itPath->first;
                        }
                }
	}
	return strPreferredSrm;
}

string libSbNet::getDefaultFirst()
{
	//searches the default SE
	//builds  map of "srm::filename->host"

	string strDefaultSrm;

	//get the local default host
	string strDefaultHost;
        char * defaultSe=getenv("VO_SUPERBVO_ORG_DEFAULT_SE");
	if (defaultSe != NULL)
	{
		strDefaultHost.append(defaultSe);
        }	
	
	
	string prefix("srm://");

	char * sourceFile = const_cast<char*> ( inputLFNString.c_str() );
        char *conf_file = NULL;                         
        char * vo=getenv("LCG_GFAL_VO");                
        int insecure = 0;                                                                                      
        char ** pfns;                                                   
	
	//lists the file replicas                                                                        
        if(lcg_lr(sourceFile,vo, conf_file, insecure, &pfns)!=0){       
                perror("Error with lcg_lr!");                           
        } else {
		for (char **pfnsIter=pfns; *pfnsIter!=NULL; pfnsIter++) {
                        string strName(*pfnsIter);
                        string strPath = strName.substr(prefix.length());
  	                size_t pos = strPath.find("/");
                       	string strHost= strPath.substr(0, pos);
	
			//returns to the lib the default host only if it is present in the file replicas list
			if (strHost == strDefaultHost) strDefaultSrm = strName;

       	                //map path building...  
       	                mapSrmHost[strName]=strHost;
		}
	}
	free(pfns);

	//if not found in the lr is empty
	return strDefaultSrm;
}


string libSbNet::sbGetTurl(string inputString)
{

        if (debugStatus) cout << "Lib Input String: " << inputString << endl;	
	

	inputLFNString = inputString;

	string strSrm;

	//Try first the default Srm, builds mapSrmHost (srm-host)
	strSrm=getDefaultFirst();	

	if(debugStatus) cout<<"strDefault: " << strSrm << endl;
	
	//otherwise searches the fastest one
	if (strSrm.empty())
	{
		//returns the lowest ping srm and builds the lib static map of host-pingtime
		strSrm = findStoragePing();
 	}

	int retValue = 1;
	string strOut;

	
	if (!strSrm.empty())
	{
		string strProtocol;
		istringstream iss(supProt, istringstream::in);

		while( iss >> strProtocol && retValue != 0)     
		{
			
			if (strProtocol == "file")
			{
				string strLocalFile;
				//the first srm is the fastest. The function searches over the entire map
				retValue = protAccess(strProtocol, strSrm, strLocalFile);

				if(retValue == 0)
				{
					if(debugStatus)
					{
						cout << "Local FileName: " << strLocalFile << endl;
					}
					strOut = strLocalFile;
				}
				
			}	
			else if (strProtocol == "gsiftp")
			{
				string strCpDestFile;	

				char * pwd=getenv("PWD");
				strCpDestFile.append("file://");
				strCpDestFile.append(pwd);
				strCpDestFile.append("/");

				size_t found=inputLFNString.rfind("/");
				if (found != string::npos)
				{
					strCpDestFile.append((inputLFNString.substr(found+1)).c_str());
				}
				
				if(debugStatus)
				{
					cout << "Local Copy DestFileName: " << strCpDestFile << endl;
				}

				//the first srm is the fastest. The function searches over the entire map
				retValue = localCopy(strSrm, strCpDestFile);	
				
				if(retValue == 0)
				{
					strOut = strCpDestFile;
				}
					
			}
		}
	}
	return strOut;
}


int libSbNet::protAccess(string strProtocol, string strSrm, string& strLocalFile)
{
	int retValue = 0;
        char* surl = const_cast<char*> ( strSrm.c_str() );
        char* protocol = const_cast<char*> ( strProtocol.c_str() );

	char* turl = NULL;
	int reqId;
	int fileId;
	char* token;
	

	//int lcg_gt (char *surl, char *protocol, char **turl, int *reqid, int *fileid, char **token);
	retValue = lcg_gt(surl, protocol, &turl, &reqId, &fileId, &token);

	if (debugStatus){
		cout << endl;
		cout << "lcg_gt retValue: " << retValue << endl;
		cout << "SURL : " << surl << endl;
		cout << "TURL : " << turl << endl;
		cout << endl;
	}


	 //temporary erase from mapSrmHost  
        map<string,string>::iterator itPath;
        itPath=mapSrmHost.find(strSrm); 
        
        string strTempSrm=itPath->first;
        string strTempHost=itPath->second;
                
        mapSrmHost.erase(itPath);


	if (retValue == 0)
	{
		if (turl != NULL)
		{
			//ok, TURL found!
			string strFile(turl);
			string strPref="file://";
			size_t protPos = strFile.find(strPref);
        		string strPath = strFile.substr(protPos+strPref.length());

			if(debugStatus) cout << "fileAccess::strPath: " << strPath << endl;
	
			//check if the file is really accessible
			if(fexists(strPath.c_str()))
			{
				strLocalFile.append(turl);
			}
			else
			{
				//the file is not accessible
				retValue=1;
			}
		}
	}


	
	if(retValue != 0) 
	{
		//problems with lcg_gt or the file is not accessible
		//retry with the fastest one
		string strPreferredSrm = searchFastStorage();

                if (!strPreferredSrm.empty())
                {
                        //recursive
			retValue = protAccess(strProtocol, strPreferredSrm, strLocalFile);	
                }
	}

        //re-add the failed srm to the map
        mapSrmHost[strTempSrm]=strTempHost;	

	return retValue;	
}

bool libSbNet::fexists(const char *filename)
{		
	//returns true if the file exists
	ifstream ifile(filename);
	return ifile;
}	



